#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include "cmd.h"

void exec_cmd(struct cmd *cmd) // 执行单个命令
{
    if (cmd->input)
    {
        int fd = open(cmd->input, O_RDONLY);
        if (fd < 0)
        {
            perror("open input");
            exit(0);
        }
        dup2(fd, STDIN_FILENO);
        close(fd);
    }
    if (cmd->output)
    {
        int fd = open(cmd->output, O_WRONLY | O_CREAT | O_TRUNC, 0666);
        if (fd < 0)
        {
            perror("open output");
            exit(0);
        }
        dup2(fd, STDOUT_FILENO);
        close(fd);
    }
    int error = execvp(cmd->argv[0], cmd->argv);
    if (error < 0)
    {
        perror("execvp");
        exit(0);
    }
}

int builtin_cmd(struct cmd *cmd) // 执行内置命令
{
    if (strcmp(cmd->argv[0], "cd") == 0)
    {
        chdir(cmd->argv[1]);
        char *path = getcwd(NULL, 0);
        printf("current working directory:%s\n", path);
    }
    else if (strcmp(cmd->argv[0], "exit") == 0)
        exit(0);
    else if (strcmp(cmd->argv[0], "pwd") == 0)
    {
        char workdir[100];
        getcwd(workdir, 100);
        printf("current working directory:%s\n", workdir);
    }
    else
        return 0;
    return 1;
}

void exec_pipe_cmd(int cmdc, struct cmd *cmdv)
{
    if (strcmp(cmdv->argv[0], "exit") == 0) 
    {
        exit(0);
    }
    int fds[2]; // 创建管道
    int in_fd = STDIN_FILENO; // 输入端默认为标准输入
    for (int i = 0; i < cmdc; i++) {
        struct cmd *cmd = &cmdv[i];
        if (pipe(fds) == -1) { // 创建管道失败
            perror("pipe");
            exit(1);
        }
        pid_t pid = fork(); // 开子进程，执行各个命令
        if (pid == -1) { // 创建子进程失败
            perror("fork");
            exit(1);
        } else if (pid == 0) { // 子进程
            if (i > 0) { // 不是第一个命令，输入从管道中读取
                if (dup2(in_fd, STDIN_FILENO) == -1) {
                    perror("dup2");
                    exit(1);
                }
                close(fds[0]); // 关闭管道的输出端
            }
            if (i < cmdc - 1) { // 不是最后一个命令，输出到管道中
                if (dup2(fds[1], STDOUT_FILENO) == -1) {
                    perror("dup2");
                    exit(1);
                }
            }
            if (builtin_cmd(cmd) == 0) {
                exec_cmd(cmd);
            }
            exit(0);
        } 
        else 
        { // 父进程
            wait(NULL); // 等待子进程执行完毕
            if (i > 0) { // 不是第一个命令，关闭管道输入端
                close(in_fd);
            }
            close(fds[1]); // 关闭管道输出端
            in_fd = fds[0]; // 下一个命令从管道输入端读取
        }
    }
}

void read_and_cmp(char *path, char *expected)
{
    int fd = open(path, O_RDONLY);

    char buff[1024];
    int count = read(fd, buff, sizeof(buff));
    buff[count] = 0;

    close(fd);
    assert(strcmp(buff, expected) == 0);
}

void test_exec_cmd()
{
    unlink("test.out");

    pid_t pid = fork();
    if (pid == 0) { 
        struct cmd cmd = {
            2, {"echo", "hello", NULL}, NULL, "test.out"
        };
        exec_cmd(&cmd);
    }
    wait(NULL);

    read_and_cmp("test.out", "hello\n");
    printf("test_exec_cmd\t\tpass\n");
}

// cat < test.in | sort | uniq | cat > test.out
void test_exec_pipe_cmd()
{
    unlink("test.out");

       struct cmd cmdv[] = {
    {
        .argc = 1,
        .argv = {"cat",NULL},
        .input = "test.in",
        .output = NULL
    },
    {
        .argc = 1,
        .argv = {"sort",NULL},
        .input = NULL,
        .output = NULL,
    },
    {
        .argc=1,
        .argv={"uniq",NULL},
        .input=NULL,
        .output=NULL,
    },
    {
        .argc=1,
        .argv={"cat",NULL},
        .input=NULL,
        .output="test.out",
    }
};
        exec_pipe_cmd(4, cmdv);

    read_and_cmp("test.out", "1\n2\n3\n");
    printf("test_exec_pipe_cmd\tpass\n");
}
